/*
* 首先获取列表组的每个Li的区间高度
* 再通过监听滚动事件onScroll，拿到实时滚动的位置
* 在watch滚动位置，判断滚动的Y值落在那个区间内，并拿到当前的索引
* 通过索引在拿到当前组固定标题是什么
* setup书写顺序 将变量定义写在前面，后面是钩子函数和主要逻辑 再后面是辅助方法
* */
import { ref, watch, computed, nextTick } from 'vue'

export default function useFixed (props) {
  const TITLE_HEIGHT = 30
  const groupRef = ref(null)
  // 记录每个Li的高度区间 因为是响应式 也用ref,因为是一个空数组 所以ref([])
  const listHeights = ref([])
  // 存放滚动的数值
  const scrollY = ref(0)
  // 当前组li的索引
  const currentIndex = ref(0)
  // 偏移量 当前组下方距离顶部标题栏fixTitle底部的距离,计算逻辑就是每个组li的底部高度heightBottom减去当前滚动的偏移量pos.y(newY)
  const distance = ref(0)

  // 计算属性注意也需要引入 顶部固定的标题栏的值
  const fixedTitle = computed(() => {
    // 当拉上去的时候，不希望有fixedtitle
    if (scrollY.value < 0) {
      return ''
    }
    // 通过currentIndex拿到当前的组
    const currentGroup = props.data[currentIndex.value]
    return currentGroup ? currentGroup.title : ''
  })

  // 使用CSS3实现滚动时组底部接近固定标题，固定标题有个向上的偏移 使用计算属性得到样式
  // 通过距离计算样式
  const fixedStyle = computed(() => {
    const distanceVal = distance.value
    // 判断是否满足偏移的条件，如果不满足条件那么就是没接近顶部就是0
    const diff = (distanceVal > 0 && distanceVal < TITLE_HEIGHT) ? distanceVal - TITLE_HEIGHT : 0
    return {
      transform: `translate3d(0, ${diff}px, 0)`
    }
  })

  // 使用watchAPI也需要引入,vue3的watch要监听一个getter函数，() => props.data就是监听的数据，后面就是监听到变化后执行的函数
  watch(() => props.data, async () => {
    // 因为监听到数据的变化后，DOM不一定会立即发生变化，这是一个异步过程 所以等nextTick DOM更新完后再进行计算高度
    await nextTick()
    calculate()
  })

  // watch可以监听一个 getter 函数，也可以 watch 一个 ref 或者是 reactive 的响应式对象
  // 根据滚动位置判断滚动到哪个区间
  watch(scrollY, (newY) => {
    // 对比Y值落在那个区间内
    const listHeightsVal = listHeights.value
    // 这里-1原因是listHeights的区间值是实际比其中的li要多一个，因为开始push了一个0
    for (let i = 0; i < listHeightsVal.length - 1; i++) {
      // 取区间的顶部和底部做对比
      const heightTop = listHeightsVal[i]
      const heightBottom = listHeightsVal[i + 1]
      if (newY >= heightTop && newY <= heightBottom) {
        // 求得当前滚动位置落在哪个组上
        currentIndex.value = i
        distance.value = heightBottom - newY
      }
    }
  })

  // 当歌手数据变化后 进行计算
  function calculate () {
    // 获取歌手列表外层ul，并获取ul下每个li的DOM元素
    const list = groupRef.value.children
    const listHeightsVal = listHeights.value
    // 记录区间高度
    let height = 0
    // 初始化操作将数组清空 因为是ref对象所以实际的值保存在value中
    listHeightsVal.length = 0
    // 从0开始，将第一个值存入数组
    listHeightsVal.push(height)
    // list是DOM类数组的结构
    for (let i = 0; i < list.length; i++) {
      // 高度是一个累加的值 类似于楼梯
      height += list[i].clientHeight
      listHeightsVal.push(height)
    }
  }

  // 当scroll组件滚动的时候，接收触发的方法
  function onScroll (pos) {
    // 因为派发的Y值是从0到负的，所以要在前面加负号 但是height是一个正值
    scrollY.value = -pos.y
  }

  return {
    groupRef,
    onScroll,
    fixedTitle,
    fixedStyle,
    currentIndex
  }
}
